﻿namespace IntellectShop.Web2.Hardware.Models
{
	using System;
	using System.ComponentModel;
	using System.ComponentModel.DataAnnotations;
	using System.Globalization;
	using System.Net.Mail;
	using System.Web.Security;
	using IntellectShop.Common;
	using System.Configuration;

	#region Models

	[PropertiesMustMatch("NewPassword", "ConfirmPassword", ErrorMessage = "Новый пароль и подтверждающий пароль не совпадают.")]
	public class ChangePasswordModel
	{
		[Required]
		[DataType(DataType.Password)]
		[DisplayNameLocalized(typeof(Resources.Account), "CurrentPassword")]
		public string OldPassword { get; set; }

		[Required]
		[ValidatePasswordLength]
		[DataType(DataType.Password)]
		[DisplayNameLocalized(typeof(Resources.Account), "NewPassword")]
		public string NewPassword { get; set; }

		[Required]
		[DataType(DataType.Password)]
		[DisplayNameLocalized(typeof(Resources.Account), "ConfirmNewPassword")]
		public string ConfirmPassword { get; set; }
	}

	public class LogOnModel
	{
		[Required]
		[DisplayNameLocalized(typeof(Resources.Account), "UserName")]
		public string UserName { get; set; }

		[Required]
		[DataType(DataType.Password)]
		[DisplayNameLocalized(typeof(Resources.Account), "Password")]
		public string Password { get; set; }

		[DisplayNameLocalized(typeof(Resources.Account), "RememberMe")]
		public bool RememberMe { get; set; }
	}

	[PropertiesMustMatch("Password", "ConfirmPassword", ErrorMessage = "Новый пароль и подтверждающий пароль не совпадают.")]
	public class RegisterModel
	{
		[Required]
		[DisplayNameLocalized(typeof(Resources.Account), "UserName")]
		public string UserName { get; set; }

		[Required]
		[DataType(DataType.EmailAddress)]
		[DisplayNameLocalized(typeof(Resources.Account), "EmailAddress")]
		public string Email { get; set; }

		[Required]
		[ValidatePasswordLength]
		[DataType(DataType.Password)]
		[DisplayNameLocalized(typeof(Resources.Account), "Password")]
		public string Password { get; set; }

		[Required]
		[DataType(DataType.Password)]
		[DisplayNameLocalized(typeof(Resources.Account), "ConfirmPassword")]
		public string ConfirmPassword { get; set; }
	}
	#endregion

	#region Services
	// The FormsAuthentication type is sealed and contains static members, so it is difficult to
	// unit test code that calls its members. The interface and helper class below demonstrate
	// how to create an abstract wrapper around such a type in order to make the AccountController
	// code unit testable.

	public interface IMembershipService
	{
		int MinPasswordLength { get; }
		int MinNonAplhaNumerics { get; }

		bool ValidateUser(string userName, string password);
		MembershipCreateStatus CreateUser(string userName, string password, string email);
		bool ChangePassword(string userName, string oldPassword, string newPassword);
		void ResetPassword(string userName, out string newPassword);
		string GetUserEmail(string userName);
		MembershipUserCollection GetAllUsers();
		MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords);
		MembershipUser GetUser(string userName, bool userIsOnline);
		void UpdateUser(MembershipUser user);
		bool DeleteUser(string userName, bool deleteAllRelatedData);
	}

	public class AccountMembershipService : IMembershipService
	{
		private readonly MembershipProvider _provider;

		#region IMembershipService Members

		public AccountMembershipService()
			: this(null)
		{
		}

		public AccountMembershipService(MembershipProvider provider)
		{
			_provider = provider ?? Membership.Provider;
		}

		public int MinPasswordLength
		{
			get
			{
				return _provider.MinRequiredPasswordLength;
			}
		}

		public bool ValidateUser(string userName, string password)
		{
			if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Значение не может быть пустым.", "userName");
			if (String.IsNullOrEmpty(password)) throw new ArgumentException("Значение не может быть пустым.", "password");

			return _provider.ValidateUser(userName, password);
		}

		public MembershipCreateStatus CreateUser(string userName, string password, string email)
		{
			if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Значение не может быть пустым.", "userName");
			if (String.IsNullOrEmpty(password)) throw new ArgumentException("Значение не может быть пустым.", "password");
			if (String.IsNullOrEmpty(email)) throw new ArgumentException("Значение не может быть пустым.", "email");

			MembershipCreateStatus status;
			_provider.CreateUser(userName, password, email, null, null, true, null, out status);
			return status;
		}

		public bool ChangePassword(string userName, string oldPassword, string newPassword)
		{
			if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Значение не может быть пустым.", "userName");
			if (String.IsNullOrEmpty(oldPassword)) throw new ArgumentException("Значение не может быть пустым.", "oldPassword");
			if (String.IsNullOrEmpty(newPassword)) throw new ArgumentException("Значение не может быть пустым.", "newPassword");

			// The underlying ChangePassword() will throw an exception rather
			// than return false in certain failure scenarios.
			try
			{
				MembershipUser currentUser = _provider.GetUser(userName, true /* userIsOnline */);
				return currentUser.ChangePassword(oldPassword, newPassword);
			}
			catch (ArgumentException)
			{
				return false;
			}
			catch (MembershipPasswordException)
			{
				return false;
			}
		}

		public void ResetPassword(string userName, out string newPassword)
		{
			MembershipUser user = GetUser(userName, false);

			// Generate a new password
			newPassword = user.ResetPassword();
		}

		public string GetUserEmail(string userName)
		{
			if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Значение не может быть пустым.", "userName");

			MembershipUser currentUser = _provider.GetUser(userName, true /* userIsOnline */);
			return currentUser.Email;
		}

		public int MinNonAplhaNumerics
		{
			get { return _provider.MinRequiredNonAlphanumericCharacters; }
		}

		public MembershipUserCollection GetAllUsers()
		{
			int totalRecords;
			return GetAllUsers(0, int.MaxValue, out totalRecords);
		}

		public MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
		{
			return _provider.GetAllUsers(pageIndex, pageSize, out totalRecords);
		}

		public MembershipUser GetUser(string userName, bool userIsOnline)
		{
			return _provider.GetUser(userName, userIsOnline);
		}

		public void UpdateUser(MembershipUser user)
		{
			_provider.UpdateUser(user);
		}

		public bool DeleteUser(string userName, bool deleteAllRelatedData)
		{
			return _provider.DeleteUser(userName, deleteAllRelatedData);
		}

		#endregion
	}

	public interface IFormsAuthenticationService
	{
		void SignIn(string userName, bool createPersistentCookie);
		void SignOut();
	}

	public class FormsAuthenticationService : IFormsAuthenticationService
	{
		public void SignIn(string userName, bool createPersistentCookie)
		{
			if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Значение не может быть пустым.", "userName");

			FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
		}

		public void SignOut()
		{
			FormsAuthentication.SignOut();
		}
	}

	public interface IRoleService
	{
		string[] GetAllRoles();
		string[] GetUsersInRole(string role);
		void RemoveUserFromRole(string userName, string roleName);
		void RemoveUsersFromRole(string[] userNames, string role);
		void RemoveUsersFromRoles(string[] userNames, string[] roleNames);
		bool DeleteRole(string roleName, bool throwOnPopulatedRole);
		bool RoleExists(string roleName);
		void CreateRole(string roleName);
		bool IsUserInRole(string userName, string roleName);
		void AddUserToRole(string userName, string roleName);
		void AddUsersToRoles(string[] userName, string[] roleName);
	}

	public class RoleService : IRoleService
	{
		private readonly RoleProvider _provider;

		public RoleService()
			: this(null)
		{
		}

		public RoleService(RoleProvider provider)
		{
			_provider = provider ?? Roles.Provider;
		}

		public string[] GetAllRoles()
		{
			return _provider.GetAllRoles();
		}

		public string[] GetUsersInRole(string roleName)
		{
			return _provider.GetUsersInRole(roleName);
		}

		public void RemoveUserFromRole(string userName, string roleName)
		{
			RemoveUsersFromRoles(new[] { userName }, new[] { roleName });
		}

		public void RemoveUsersFromRole(string[] userNames, string roleName)
		{
			RemoveUsersFromRoles(userNames, new[] { roleName });
		}

		public void RemoveUsersFromRoles(string[] userNames, string[] roleNames)
		{
			_provider.RemoveUsersFromRoles(userNames, roleNames);
		}

		public bool DeleteRole(string roleName, bool throwOnPopulatedRole)
		{
			return _provider.DeleteRole(roleName, throwOnPopulatedRole);
		}

		public bool RoleExists(string roleName)
		{
			return _provider.RoleExists(roleName);
		}

		public void CreateRole(string roleName)
		{
			_provider.CreateRole(roleName);
		}

		public bool IsUserInRole(string userName, string roleName)
		{
			return _provider.IsUserInRole(userName, roleName);
		}

		public void AddUserToRole(string userName, string roleName)
		{
			AddUsersToRoles(new[] { userName }, new[] { roleName });
		}

		public void AddUsersToRoles(string[] userNames, string[] roleNames)
		{
			_provider.AddUsersToRoles(userNames, roleNames);
		}
	}

	#endregion

	#region Validation
	public static class AccountValidation
	{
		public static string ErrorCodeToString(MembershipCreateStatus createStatus)
		{
			// See http://go.microsoft.com/fwlink/?LinkID=177550 for
			// a full list of status codes.
			switch (createStatus)
			{
				case MembershipCreateStatus.DuplicateUserName:
					return "Username already exists. Please enter a different user name.";

				case MembershipCreateStatus.DuplicateEmail:
					return "A username for that e-mail address already exists. Please enter a different e-mail address.";

				case MembershipCreateStatus.InvalidPassword:
					return "The password provided is invalid. Please enter a valid password value.";

				case MembershipCreateStatus.InvalidEmail:
					return "The e-mail address provided is invalid. Please check the value and try again.";

				case MembershipCreateStatus.InvalidAnswer:
					return "The password retrieval answer provided is invalid. Please check the value and try again.";

				case MembershipCreateStatus.InvalidQuestion:
					return "The password retrieval question provided is invalid. Please check the value and try again.";

				case MembershipCreateStatus.InvalidUserName:
					return "The user name provided is invalid. Please check the value and try again.";

				case MembershipCreateStatus.ProviderError:
					return "The authentication provider returned an error. Please verify your entry and try again. If the problem persists, please contact your system administrator.";

				case MembershipCreateStatus.UserRejected:
					return "The user creation request has been canceled. Please verify your entry and try again. If the problem persists, please contact your system administrator.";

				default:
					return "An unknown error occurred. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
			}
		}
	}

	[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
	public sealed class PropertiesMustMatchAttribute : ValidationAttribute
	{
		private const string _defaultErrorMessage = "'{0}' and '{1}' do not match.";
		private readonly object _typeId = new object();

		public PropertiesMustMatchAttribute(string originalProperty, string confirmProperty)
			: base(_defaultErrorMessage)
		{
			OriginalProperty = originalProperty;
			ConfirmProperty = confirmProperty;
		}

		public string ConfirmProperty { get; private set; }
		public string OriginalProperty { get; private set; }

		public override object TypeId
		{
			get
			{
				return _typeId;
			}
		}

		public override string FormatErrorMessage(string name)
		{
			return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
				OriginalProperty, ConfirmProperty);
		}

		public override bool IsValid(object value)
		{
			PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
			object originalValue = properties.Find(OriginalProperty, true /* ignoreCase */).GetValue(value);
			object confirmValue = properties.Find(ConfirmProperty, true /* ignoreCase */).GetValue(value);
			return Object.Equals(originalValue, confirmValue);
		}
	}

	[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
	public sealed class ValidatePasswordLengthAttribute : ValidationAttribute
	{
		private const string _defaultErrorMessage = "'{0}' must be at least {1} characters long.";
		private readonly int _minCharacters = Membership.Provider.MinRequiredPasswordLength;

		public ValidatePasswordLengthAttribute()
			: base(_defaultErrorMessage)
		{
		}

		public override string FormatErrorMessage(string name)
		{
			return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
				name, _minCharacters);
		}

		public override bool IsValid(object value)
		{
			string valueAsString = value as string;
			return (valueAsString != null && valueAsString.Length >= _minCharacters);
		}
	}
	#endregion
}
